package server

import (
	"context"
	"runtime"
	"strconv"
	"strings"

	"github.com/go-kratos/kratos/v2/middleware/tracing"

	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware"
	"github.com/go-kratos/kratos/v2/transport/grpc"
	"github.com/go-kratos/kratos/v2/transport/http"

	"gitee.com/shuokeyun/kratos/conf"
	logv1 "gitee.com/shuokeyun/kratos/log"
)

// WithOption .
type WithOption func(app *App)

func (w WithOption) apply(app *App) {
	w(app)
}

// WithAfterGRPCServerFunc grpc服务创建后的回调方法
type WithAfterGRPCServerFunc func(*grpc.Server) error

// WithAfterHTTPServerFunc http服务创建后的回调方法
type WithAfterHTTPServerFunc func(*http.Server) error

// WithConfigPath 设置配置文件目录
func WithConfigPath(filePath string) WithOption {
	return func(app *App) {
		app.configFilePath = filePath
	}
}

// WithConfig 设置自定义文件加载的结构体
func WithConfig(co interface{}) WithOption {
	return func(app *App) {
		app.appConfBootstrap = co
	}
}

// WithAfterGRPCServer grpc服务创建后的回调
func WithAfterGRPCServer(f WithAfterGRPCServerFunc) WithOption {
	return func(app *App) {
		app.withAfterGRPCServer = f
	}
}

// WithAfterHTTPServer http服务创建后的回调
func WithAfterHTTPServer(f WithAfterHTTPServerFunc) WithOption {
	return func(app *App) {
		app.withAfterHTTPServer = f
	}
}

// WithLogger 指定日志生成器
func WithLogger(f func(*conf.Bootstrap) log.Logger) WithOption {
	return func(app *App) {
		app.logger = f(app.GetConfigBootstrap())
	}
}

// WithInitApp 指定初始化方法
func WithInitApp(f func(*App, log.Logger) ([]WithOption, func(), error)) WithOption {
	return func(app *App) {
		app.withInitApp = f
	}
}

// WithGrpcMiddleware 新增grpc服务中间件
func WithGrpcMiddleware(m ...middleware.Middleware) WithOption {
	return func(app *App) {
		app.grpcMiddleware = m
	}
}

// WithHttpMiddleware 新增http服务中间件
// nolint:revive
func WithHttpMiddleware(m ...middleware.Middleware) WithOption {
	return func(app *App) {
		app.httpMiddleware = m
	}
}

// WithDefaultLogger 默认日志
func WithDefaultLogger() WithOption {
	return WithLogger(func(bootstrap *conf.Bootstrap) log.Logger {
		return log.With(logv1.NewLogger(bootstrap.GetLogging()),
			"caller", caller(3),
			"service.id", bootstrap.GetServer().GetId(),
			"service.name", bootstrap.GetServer().GetName(),
			"service.version", bootstrap.GetServer().GetVersion(),
			"trace_id", tracing.TraceID(),
			"span_id", tracing.SpanID(),
		)
	})
}

func caller(depth int) log.Valuer {
	return func(context.Context) interface{} {
		pc := make([]uintptr, 4)
		n := runtime.Callers(depth, pc)
		pc = pc[:n]
		frams := runtime.CallersFrames(pc)
		var file string
		var line int
		for f, _ := frams.Next(); f.Line > 0; f, _ = frams.Next() {
			if strings.LastIndex(f.File, "/log/log.go") > 0 || strings.LastIndex(f.File, "/log/filter.go") > 0 || strings.LastIndex(f.File, "/log/helper.go") > 0 {
				continue
			}
			file = f.File
			line = f.Line
			break
		}
		li := strings.Split(file, "/")
		return strings.Join(li[len(li)-3:], "/") + ":" + strconv.Itoa(line)
	}
}
